using System;
using System.Collections.Generic;
using System.Text;
using EnvDTE80;
using EnvDTE;
using Koda.Utils;
using Koda.VisualStudio;

namespace Koda.Model
{
    public class PropertyGenerator
    {
        private bool generateComments;
        private CodeClass2 codeClass;
        private Property[] properties;

        public Property[] Properties
        {
            get
            {
                return this.properties;
            }
        }
	

        public CodeClass2 CodeClass
        {
            get
            {
                return this.codeClass;
            }
        }	

        public bool GenerateComments
        {
            get
            {
                return this.generateComments;
            }
            set
            {
                this.generateComments = value;
            }
        }

        public PropertyGenerator(CodeClass2 codeClass)
        {
            this.codeClass = codeClass;
            this.generateComments = false;
            this.Initialize();
        }

        public bool IsSelected(Property property)
        {
            return false;
        }

        public void SelectAll()
        {
            this.UpdateAll(PropertyOptions.GetSet);
        }

        public void SelectNone()
        {
            this.UpdateAll(PropertyOptions.None);
        }

        public void SelectAllNonExisting()
        {          
            foreach (Property property in this.properties)
            {
                if (VsCodeModelUtilities.ContainsMember(this.codeClass, property.Name))
                {
                    property.Options = PropertyOptions.None;
                }
                else
                {
                    property.Options = PropertyOptions.GetSet;
                }
            }
        }

        public void SelectAllGetters()
        {
            this.UpdateAll(PropertyOptions.Get);
        }

        public void SelectAllSetters()
        {
            this.UpdateAll(PropertyOptions.Set);
        }

        private void UpdateAll(PropertyOptions options)
        {
            foreach (Property property in this.properties)
            {
                property.Options = options;
            }        
        }

        public void Generate()
        {
            DTE dte = this.codeClass.DTE;
            dte.UndoContext.Open("Generate Properties", false);

            try
            {
                CodeGenerator codeGenerator = CodeGeneratorFactory.Create(this.codeClass.Language);

                foreach (Property property in this.properties)
                {
                    GenerateProperty(codeGenerator, property);
                }
            }
            catch (Exception)
            {
                dte.UndoContext.SetAborted();
                throw;
            }
            finally
            {
                if (dte.UndoContext.IsOpen)
                {
                    dte.UndoContext.Close();
                }
            }
        }

        private void GenerateProperty(CodeGenerator codeGenerator, Property property)
        {
            if (VsCodeModelUtilities.ContainsMember(this.codeClass, property.Name))
                return;

            if (property.Options == PropertyOptions.None)
                return;

            CodeProperty codeProperty = (CodeProperty)this.codeClass.AddProperty(
                property.Name,
                property.Name,
                property.CodeTypeRef,
                -1,
                property.Visibility,
                null);

            if (this.generateComments)
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendLine("<doc>");
                sb.AppendLine("<summary>");
                sb.AppendLine();
                sb.AppendLine("</summary>");
                sb.Append("</doc>");

                codeProperty.DocComment = sb.ToString();
            }

            if (property.Options == PropertyOptions.Get || property.Options == PropertyOptions.GetSet)
            {
                EditPoint2 editPoint = (EditPoint2)codeProperty.Getter.GetStartPoint(vsCMPart.vsCMPartBody).CreateEditPoint();
                editPoint.EndOfLine();
                int position = editPoint.LineCharOffset;
                editPoint.StartOfLine();
                editPoint.Delete(position);
                editPoint.Insert(codeGenerator.GenerateReturnStatement(property.VariableName));
                editPoint.SmartFormat(editPoint);
            }

            if (property.Options == PropertyOptions.Set || property.Options == PropertyOptions.GetSet)
            {
                EditPoint2 editPoint = (EditPoint2)codeProperty.Setter.GetStartPoint(vsCMPart.vsCMPartBody).CreateEditPoint();
                editPoint.Insert(codeGenerator.GenerateAssignStatement(property.VariableName, "value"));
                editPoint.SmartFormat(editPoint);
            }
        }

        private void Initialize()
        {
            List<Property> properties = new List<Property>();

            foreach (CodeElement codeElement in this.codeClass.Members)
            {
                if (codeElement.Kind == vsCMElement.vsCMElementVariable)
                {
                    CodeVariable2 codeVariable = codeElement as CodeVariable2;
                    if (codeVariable != null && codeVariable.ConstKind == vsCMConstKind.vsCMConstKindNone)
                    {
                        string variableName = codeVariable.Name;
                        string propertyName = this.ConvertToPropertyName(variableName);
                        CodeTypeRef codeTypeRef = codeVariable.Type;

                        Property property = new Property(variableName, propertyName, codeTypeRef);
                        properties.Add(property);
                    }
                }
            }

            properties.Sort();

            this.properties = properties.ToArray();
        }

        private string ConvertToPropertyName(string variableName)
        {
            if (variableName.StartsWith("_"))
            {
                variableName = variableName.Substring(1);
            }
            else if (variableName.StartsWith("m_"))
            {
                variableName = variableName.Substring(2);
            }

            return variableName.Substring(0, 1).ToUpper() + variableName.Substring(1, variableName.Length - 1);  
        }
    }
}
